﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Castle.ActiveRecord;
using Castle.ActiveRecord.Framework;

namespace Atendex.Infraestrutura.Repositórios
{
    public abstract class RepositorioGenerico<T> 
    {
        /// <summary>
        /// Salva uma entidade no banco de dados. Caso o Id seja um valor inválido,
        /// a entidade é criada; caso contrário ela é persistida.
        /// </summary>
        /// <param name="instancia"></param>
        public virtual void Salvar(T instancia)
        {
            try
            {
                ConverteParaAR(instancia).Save();
            }
            catch (ActiveRecordException ex)
            {
                throw new RepositorioException("Houve um erro ao salvar o objeto.", ex);
            }
        }

        protected ActiveRecordBase ConverteParaAR(T instancia)
        {
            ActiveRecordBase instanciaAR = instancia as ActiveRecordBase;
            if (instanciaAR != null) return instanciaAR;
            else throw new RepositorioException("O objeto passado não é do tipo ActiveRecordBase");
        }



        protected abstract Type ImpType
        {
            get;
        }


        /// <summary>
        /// Obtém um objeto a partir de seu id, e lança um RepositorioException caso não o encontre.
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public T ObtemPorId(object id)
        {
             try
             {
                 Type tipoGenerico = typeof(ActiveRecordBase<>);
                 Type tipoGenericoComParametros = tipoGenerico.MakeGenericType(ImpType);
                 T t = (T)tipoGenericoComParametros.GetMethod("TryFind").Invoke(null, new object[] { id });
                 if (t != null) return t;
                 else throw new RepositorioException("Entidade de id='" + id + "' não existe.");
             }
             catch (ActiveRecordException e)
             {
                 throw new RepositorioException("Houve um erro no acesso aos dados", e);
             }
        }
        /// <summary>
        /// Obtém um objeto a partir de seu id, e retorna nulo caso não encontre.
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public abstract T TentaObterPorId(object id);
        /// <summary>
        /// Obtém um objeto a partir de um valor de propriedade.
        /// </summary>
        /// <param name="propriedade"></param>
        /// <param name="valor"></param>
        /// <returns></returns>
        public T[] ObtemPorPropriedade(string propriedade, object valor)
        {
            try
            {
                Type tipoGenerico = typeof(ActiveRecordBase<>);
                Type tipoGenericoComParametros = tipoGenerico.MakeGenericType(ImpType);
                return (T[])tipoGenericoComParametros.GetMethod("FindAllByProperty", new Type[] { typeof(string), typeof(object) }).Invoke(null, new object[] { propriedade, valor });
                //return (T[])ImpType.GetMethod("FindAllByProperty").Invoke(null, new object[] { propriedade, valor }); 
                //    ActiveRecordBase<T>.FindAllByProperty(propriedade, valor);
            }
            catch (ActiveRecordException e)
            {
                throw new RepositorioException("Houve um erro no acesso aos dados", e);
            }

        }




        /// <summary>
        /// Obtém todas as entradas salvas.
        /// </summary>
        /// <returns></returns>
        public abstract T[] ObtemTodos();
    }
}
